﻿using System;
using System.Collections.Generic;
using System.Dns.Toolkit.DnsServer;
using System.Net.DnsToolkit.Packet;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Text;

namespace DnsServer.TestUnit {
    [TestClass]
    public class IMessageDispatcherTest {

        [TestMethod]
        [HostType("Chess")]
        public void TestMethod1() {

            IMessageDispatcher dispatcher = new MockMessageDispatcher();
            var sb = new StringBuilder();
            var std = new StringDispatcher(sb);
            var itd = new IntDispatcher(sb);
            var btd = new BoolDispatcher(sb);

            dispatcher.Subscribe(std, typeof(string));
            dispatcher.Subscribe(itd, typeof(int));
            dispatcher.Subscribe(btd, typeof(bool));

            dispatcher.Publish("Hello world!");
            dispatcher.Publish(100);
            dispatcher.Publish(true);
            dispatcher.Publish("Hello baby");
            

            DarkAssert.StringContains(sb.ToString(), new[] {"Hello world!", "0100", "True", "Hello baby"});

        }

        public class StringDispatcher : ICore {
            public IMessageDispatcher Dispatcher {
                set;
                private get;
            }

            private readonly StringBuilder _sb;
            public StringDispatcher(StringBuilder sb) {
                _sb = sb;
            }
            public void MessageReceived(object s) {
                Console.WriteLine("string");
                _sb.AppendFormat("{0}", (string)s);
            }
        }

        public class IntDispatcher : ICore {
            public IMessageDispatcher Dispatcher {
                set;
                private get;
            }

            private readonly StringBuilder _sb;
            public IntDispatcher(StringBuilder sb) {
                _sb = sb;
            }
            public void MessageReceived(object i) {
                Console.WriteLine("int");
                _sb.AppendFormat("{0:0000}", (int)i);
            }
        }

        public class BoolDispatcher : ICore {
            public IMessageDispatcher Dispatcher {
                set;
                private get;
            }

            private readonly StringBuilder _sb;
            public BoolDispatcher(StringBuilder sb) {
                _sb = sb;
            }
            public void MessageReceived(object b) {
                Console.WriteLine("bool");
                _sb.AppendFormat("{0}", (bool)b);
            }
        }

        public class MockMessageDispatcher : IMessageDispatcher {

            private readonly Dictionary<Type, ICore> _actions = new Dictionary<Type, ICore>();
            
            private volatile bool _suspend;
            private int _workCount;
            private readonly AutoResetEvent _event;

            public MockMessageDispatcher() {
                _suspend = false;
                _workCount = 0;
                _event = new AutoResetEvent(false);
            }

            public void Subscribe(ICore icore, Type t) {
                lock (_actions) {
                    if (!_actions.ContainsKey(t)) {
                        _actions[t] = icore;
                    }
                }
            }

            public void WaitForFinish() {
                //_suspend = true;
                if(_workCount != 0) {
                    _event.WaitOne();
                }
            }

            public bool Publish<T>(T message) {

                var t = typeof(T);
                Console.WriteLine("{0} - Publish {1} : {2}", Thread.CurrentThread.ManagedThreadId, t.Name, message);

                if (_suspend) {
                    return false;
                }

                if (!_actions.ContainsKey(t)) {
                    return false;
                }

                Interlocked.Increment(ref _workCount);

                var workitem = new WorkItem {
                    ICore = _actions[t],
                    Message = message
                };
                ThreadPool.QueueUserWorkItem(WorkInProgress, workitem);

                return true;
            }

            void WorkInProgress(object o) {

                var workitem = (WorkItem)o;
                workitem.ICore.MessageReceived(workitem.Message);
                Interlocked.Decrement(ref _workCount);
                Console.WriteLine("WIP: {0}", _workCount);
                if (_workCount == 0) {
                    _event.Set();
                }

            }

            public class WorkItem {
                public ICore ICore { get; set; }
                public object Message { get; set; }
            }

        }

    }
}
